﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Runtime.Serialization;
using System.Globalization;

namespace SyncLib.Data
{
    [DataContract]
    public class KDataBatchHandle:IDisposable
    {
        [DataMember]
        public int TotalBatch { get; set; }
        [DataMember]
        public int BatchSize { get; set; }
        [DataMember]
        public Dictionary<int, KDataBatch> AllBatch { get; set; }
        public KDataBatchHandle CloneWithoutData()
        {
            KDataBatchHandle temp = new KDataBatchHandle();
            temp.TotalBatch = TotalBatch;
            temp.BatchSize = BatchSize;
            temp.AllBatch = new Dictionary<int, KDataBatch>();
            return temp;
        }
        public KDataBatchHandle Clone()
        {
            KDataBatchHandle temp = new KDataBatchHandle();
            temp.TotalBatch = TotalBatch;
            temp.BatchSize = BatchSize;
            temp.AllBatch = new Dictionary<int, KDataBatch>(); 
            return temp;
        }

        public void Load(DataTable tblData)
        {
            AllBatch = new Dictionary<int, KDataBatch>();
            DataSet dsData = new DataSet();
            if (BatchSize <= 0)
            {
                TotalBatch = 1;
                dsData.Tables.Add(tblData);
                AllBatch[1] = new KDataBatch(dsData, 1);
                return;
            }
            else
            {
                long maxBatch = BatchSize * 1024L;
                long currentBatchSize = 0;
                DataTable tblDataTemp = tblData.Clone();
                int rowIndex = 0;
                int batchIndex = 1;
                do
                {
                    DataRow row = tblData.Rows[rowIndex];
                    long currentRowSize = GetRowSizeFromDataRow(row);
                    if (currentRowSize > maxBatch)
                        throw new Exception("One row of table " + tblData.TableName + " has size " + currentRowSize + " it greater than maxsize " + maxBatch);
                    if (currentRowSize + currentBatchSize < maxBatch)
                    {
                        tblDataTemp.ImportRow(row);
                        currentBatchSize += currentRowSize;
                    }
                    else
                    {
                        dsData.Tables.Add(tblDataTemp);
                        AllBatch[batchIndex] = new KDataBatch(dsData, batchIndex);
                        AllBatch[batchIndex].DataSize = currentBatchSize;

                        dsData = new DataSet();
                        tblDataTemp = tblDataTemp.Clone();
                        tblDataTemp.ImportRow(row);
                        currentBatchSize = currentRowSize;
                        batchIndex++;
                    }
                    rowIndex++;
                }
                while (rowIndex < tblData.Rows.Count);
                if (currentBatchSize > 0)
                {
                    AllBatch[batchIndex] = new KDataBatch(dsData, batchIndex);
                    AllBatch[batchIndex].DataSize = currentBatchSize;
                    batchIndex++;
                }
                TotalBatch = batchIndex - 1;
            }
        }
        public void Load(IDataReader reader, string tableName)
        {
            AllBatch = new Dictionary<int, KDataBatch>();
            DataTable tblData = BuildDataTable(tableName, reader);
            DataSet dsData = new DataSet();
            dsData.Tables.Add(tblData);


            long maxBatch = BatchSize * 1024L;
            if (BatchSize <= 0)
            {
                maxBatch = long.MaxValue;
            }
            long currentBatchSize = 0;
            int rowIndex = 0;
            int batchIndex = 1;
            while (reader.Read())
            {
                DataRow row = dsData.Tables[0].NewRow();
                PopulateFullRow(reader, row);

                long currentRowSize = GetRowSizeFromDataRow(row);

                if (currentRowSize > maxBatch)
                    throw new Exception("One row of table " + tableName + " has size " + currentRowSize + " it greater than maxsize " + maxBatch);

                if (currentRowSize + currentBatchSize < maxBatch)
                {
                    dsData.Tables[0].Rows.Add(row);
                    currentBatchSize += currentRowSize;
                }
                else
                {
                    //dsData.AcceptChanges();
                    AllBatch[batchIndex] = new KDataBatch(dsData, batchIndex);
                    AllBatch[batchIndex].DataSize = currentBatchSize;
                    dsData = dsData.Clone();

                    dsData.Tables[0].Rows.Add(row.ItemArray);
                    row.RejectChanges();
                    currentBatchSize = currentRowSize;
                    batchIndex++;
                    rowIndex++;
                }
            }
            if (currentBatchSize > 0)
            {
                AllBatch[batchIndex] = new KDataBatch(dsData, batchIndex);
                AllBatch[batchIndex].DataSize = currentBatchSize;
                batchIndex++;
            }
            TotalBatch = batchIndex - 1;
        }
        public void Load(DataSet dsData)
        {
            AllBatch = new Dictionary<int, KDataBatch>();

            if (BatchSize <= 0)
            {
                TotalBatch = 1;
                AllBatch[1] = new KDataBatch(dsData.Copy(), 1);
                return;
            }

            DataSet dsDataTemp = dsData.Clone();
            long maxBatch = BatchSize * 1024L;
            long currentBatchSize = 0;
            int rowIndex = 0;
            int batchIndex = 1;
            for (int i = 0; i < dsData.Tables.Count; i++)
            {
                DataTable tblData = dsData.Tables[i];
                rowIndex = 0;
                while (rowIndex < tblData.Rows.Count)
                {
                    DataRow currentRow = tblData.Rows[rowIndex];
                    long currentRowSize = GetRowSizeFromDataRow(currentRow);
                    if (currentRowSize > maxBatch)
                    {
                        throw new Exception("One row of table " + tblData.TableName + " has size " + currentRowSize + " it greater than maxsize " + maxBatch);
                    }
                    if (currentRowSize + currentBatchSize < maxBatch)
                    {
                        dsDataTemp.Tables[tblData.TableName].ImportRow(currentRow);
                        currentBatchSize += currentRowSize;
                    }
                    else
                    {
                        AllBatch[batchIndex] = new KDataBatch(dsDataTemp, batchIndex);
                        AllBatch[batchIndex].DataSize = currentBatchSize;
                        dsDataTemp = dsDataTemp.Clone();
                        dsDataTemp.Tables[tblData.TableName].ImportRow(currentRow);
                        currentBatchSize = currentRowSize;
                        batchIndex++;                        
                    }
                    rowIndex++;
                }
            }
            if (currentBatchSize > 0)
            {
                AllBatch[batchIndex] = new KDataBatch(dsData, batchIndex);
                AllBatch[batchIndex].DataSize = currentBatchSize;
                batchIndex++;
            }
            TotalBatch = batchIndex - 1;
        }
        private DataTable BuildDataTable(string name, IDataReader enumQueryResults)
        {
            DataTable table = new DataTable(name);
            table.Locale = CultureInfo.InvariantCulture;
            for (int i = 0; i < enumQueryResults.FieldCount; i++)
            {
                table.Columns.Add(enumQueryResults.GetName(i), enumQueryResults.GetFieldType(i));
            }
            return table;
        }
        internal static long GetRowSizeFromDataRow(DataRow row)
        {
            bool flag = false;
            if (row.RowState == DataRowState.Deleted)
            {
                row.RejectChanges();
                flag = true;
            }
            long num = 0L;
            foreach (object obj2 in row.ItemArray)
            {
                string s = obj2 as string;
                byte[] buffer = obj2 as byte[];
                if (obj2 is DBNull)
                {
                    num += 5L;
                }
                else if (obj2 is Guid)
                {
                    num += 0x10L;
                }
                else if (s != null)
                {
                    num += Encoding.Unicode.GetByteCount(s);
                }
                else if (buffer != null)
                {
                    num += buffer.Length;
                }
                else
                {
                    num += GetSizeForType(obj2.GetType());
                }
            }
            if (flag)
            {
                row.Delete();
            }
            return num;
        }
        internal static long GetRowSizeFromReader(IDataReader reader)
        {
            long num = 0L;
            for (int i = 0; i < reader.FieldCount; i++)
            {

                Type fieldType = reader.GetFieldType(i);
                if (reader.IsDBNull(i))
                {
                    num += 5L;
                }
                else if (fieldType == typeof(Guid))
                {
                    num += 0x10L;
                }
                else if (fieldType == typeof(byte[]))
                {
                    num += reader.GetBytes(i, 0L, null, 0, 0);
                }
                else if (fieldType == typeof(string))
                {
                    num += reader.GetChars(i, 0L, null, 0, 0) * 2L;
                }
                else
                {
                    num += GetSizeForType(fieldType);
                }
            }
            return num;
        }

        internal static long GetSizeForType(Type type)
        {
            switch (Type.GetTypeCode(type))
            {
                case TypeCode.Empty:
                    return 0L;

                case TypeCode.Object:
                case TypeCode.Int64:
                case TypeCode.UInt64:
                case TypeCode.Double:
                case TypeCode.DateTime:
                    return 8L;

                case TypeCode.Boolean:
                case TypeCode.SByte:
                case TypeCode.Byte:
                    return 1L;

                case TypeCode.Char:
                case TypeCode.Int16:
                case TypeCode.UInt16:
                    return 2L;

                case TypeCode.Int32:
                case TypeCode.UInt32:
                case TypeCode.Single:
                    return 4L;

                case TypeCode.Decimal:
                    return 0x10L;
            }
            return 0L;
        }
        public void PopulateFullRow(IDataReader reader, DataRow row)
        {
            row.BeginEdit();
            for (int i = 0; i < reader.FieldCount; i++)
            {

                row[i] = reader.GetValue(i);

            }
            row.EndEdit();
        }

        ~KDataBatchHandle()
        {
            Dispose(false);
        }
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        protected void Dispose(bool isDisposed)
        {
            if (AllBatch != null)
            {
                foreach (KeyValuePair<int, KDataBatch> i in AllBatch)
                {
                    i.Value.Dispose();
                }
                AllBatch.Clear();
            }
        }
    }
}
